package com.blog.cloud.service;

import org.junit.jupiter.api.Test;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.concurrent.TimeUnit;

@SpringBootTest
public class BlogServiceApplicationTest {

    @Autowired
    private RedissonClient redisson;

    @Test
    public void redissonTest() {
        // 创建一个redis数据存储包装对象RBucket，其中的泛型约束值类型
        RBucket<String> bucket = redisson.getBucket("my-redis");
        // bucket.setIfAbsent("hello world");
        // 此处到源码时会执行脚本命令，向redis数据库存放数据
        bucket.set("hello world", 60, TimeUnit.SECONDS);

        // 此处会执行命令从redis中获取数据值
        String result = bucket.get();

        // redissonClient操作的每个 redis 数据对象都被封装成一个 RBucket 对象，我们通过名称key先找到redis数据，然后可以使用get方法取出其中的value值
        Object value = redisson.getBucket("blog").get();

        // 也可以像这样向redis中这只一个值，key 为 blog，value 为 hello world
        redisson.getBucket("blog").set("hello world", 60, TimeUnit.SECONDS);

        // 获取锁，如果锁不存在则创建，锁的key为自定义值，value值由框架定义生成,可以说是创建一个redis对象
        RLock lock = redisson.getLock("redis-lock");

        // 传统加锁，其他线程阻塞式等待
        // 加的锁有一个默认的过期时间30秒，同时存在一个锁自定续期机制，可以简单理解为，
        // 当业务逻辑执行时间超长，随着锁过期时间减少，一旦锁过期时间只剩20多秒而任务还未执行完成时，锁将重置过期时间为30秒
        // 同时，如果当业务执行中出现宕机而导致未能解锁，锁也会在30秒内过期，不会导致死锁。
        lock.lock();

        // 业务逻辑代码
        try {
            System.out.println("hello world");
        } catch (Exception e) {
            throw new RuntimeException("出现异常！");
        } finally {
            // 解锁
            lock.unlock();
        }

        // 自定义参数加锁
        try {
            //尝试5秒内获取锁，如果获取到了，最长60秒自动释放
            boolean flag = lock.tryLock(5, 30, TimeUnit.SECONDS);
            // 如果加锁成功，执行业务
            if (flag) {
                System.out.println("hello world");
            }
        } catch (Exception e) {
            System.out.println("获取锁失败，失败原因：" + e.getMessage());
        } finally {
            //无论如何, 最后都要解锁
            lock.unlock();
        }

        // 根据请求的类型，可以采用不同的加锁策略，以此增大并发量
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("read-write");
        // 创建读锁
        RLock readLock = readWriteLock.readLock();
        // 创建写锁
        RLock writeLock = readWriteLock.writeLock();
        try {
            // 加读锁
            readLock.lock();
            System.out.println("业务逻辑");
        } catch (Exception e) {
            System.out.println("获取锁失败，失败原因：" + e.getMessage());
        } finally {
            readLock.unlock();
            writeLock.unlock();
        }

        // 声明一个限流器
        RRateLimiter rateLimiter = redisson.getRateLimiter("redis-limit");
        // 根据请求IP地址进行限流，请求流速为每秒60，超过该值则进行限流，休息等待10秒
        rateLimiter.trySetRate(RateType.valueOf("请求IP地址"), 60, 10, RateIntervalUnit.SECONDS);
        // 尝试获取访问令牌
        boolean acquire = rateLimiter.tryAcquire();
        if (!acquire) {
            throw new RuntimeException("当前IP访问过大，请稍后访问");
        }

        // 思考，如何将加锁、限流过程做成切面，以注解的形式实现

    }

}
